#pragma once

#include "base.h"
#include "../updater/m4.h"

namespace mdtx::online::indicator
{
    template <typename T>
    class moment_raw : public indi_base<T, T, 1, 4>
    {
        using base = indi_base<T, T, 1, 4>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        impl::m4<T> m4{};
        boost::circular_buffer<T> buf;

    public:
        explicit moment_raw(ptrdiff_t period)
            : buf(period * 4)
        {
        }

        tuple_out update(tuple_in const &in) override
        {
            T x = std::get<0>(in);
            T x2 = x * x;
            T x3 = x2 * x;
            T x4 = x2 * x2;
            if (buf.full())
            {
                m4.roll_impl(x, x2, x3, x4, buf[0], buf[1], buf[2], buf[3]);
            }
            else
            {
                m4.insert_impl(x, x2, x3, x4);
            }
            bpush(buf, x, x2, x3, x4);

            return mk_tuple(m4.raw);
        }
    };

    template <typename T>
    class moment_cent : public indi_base<T, T, 1, 4>
    {
        using base = indi_base<T, T, 1, 4>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        impl::m4<T> m4{};
        boost::circular_buffer<T> buf;

    public:
        explicit moment_cent(ptrdiff_t period)
            : buf(period * 4)
        {
        }

        tuple_out update(tuple_in const &in) override
        {
            T x = std::get<0>(in);
            T x2 = x * x;
            T x3 = x2 * x;
            T x4 = x2 * x2;
            if (buf.full())
            {
                m4.roll_impl(x, x2, x3, x4, buf[0], buf[1], buf[2], buf[3]);
            }
            else
            {
                m4.insert_impl(x, x2, x3, x4);
            }
            bpush(buf, x, x2, x3, x4);

            return mk_tuple(m4.center_impl());
        }
    };

    template <typename T>
    using moment_raw_cu = detail::indi_wrap_cu<updater::m4_raw<T>>;

    template <typename T>
    using monent_cent_cu = detail::indi_wrap_cu<updater::m4_center<T>>;
}
